﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Xml.Serialization;
using Jacaranda.Data;

namespace Jacaranda.Model
{
    public class EntityList
    {
        public EntityList()
        { 
            Added = new List<Entity>();
            Removed = new List<Entity>();
            PageItems = new Entity[] { };
            PageIndex = 1;
        }

        internal bool IsLoaded { get; set; }

        public bool IsChanged { get; set; }

        internal bool IsCleared { get; set; }


        public Entity Owner { get; set; }

        internal AssociationField GetAssociationField()
        {
            return Metadata.Type(Owner.GetType()).FindAssociationField(PropertyName);
        }

        
        public virtual void Add(object item)
        { 
        }

        public void GetTotalCount()
        {
            TotalCount = GetAssociationField().GetTotalCount(Owner);
        }

        public void LoadPage(int pageSize)
        {
            PageItems = GetAssociationField().Load(Owner, PageIndex, pageSize).ToArray();
        }

        public List<Entity> Added { get; set; }

        public List<Entity> Removed { get; set; }

        public Entity[] PageItems { get; set; }

        public string PropertyName { get; set; }

        public int PageIndex { get; set; }

        public int TotalCount { get; set; }
    }

    public class EntityList<T> : EntityList where T : Entity
    {
        private List<Entity> allItems;


        public void LoadAllItems()
        {
            if (!IsLoaded)
            {
                IsLoaded = true;
                allItems = IsCleared ? new List<Entity>() : GetAssociationField().Load(Owner).ToList();

                EntityMetadata metadata = Metadata.Type(typeof(T));

                List<object> removedKeys = Removed.Select(i => metadata.GetValueOfPrimaryKey(i)).ToList();
                allItems.RemoveAll(i => removedKeys.Contains(metadata.GetValueOfPrimaryKey(i)));

                Added.ForEach(i => allItems.Add(i));
            }
        }


        #region ICollection<T> Members

        public void Add(T item)
        {
            Added.Add(item);
            IsChanged = true;

            if (IsLoaded)
            {
                allItems.Add(item);
            }
        }

        public override void Add(object item)
        {
            Add(item as T);
        }

        public void Clear()
        {
            Added.Clear();
            Removed.Clear();

            IsCleared = true;
            IsChanged = true;

            if (IsLoaded)
            {
                allItems.Clear();
            }
        }

        public bool Contains(T item)
        {
            LoadAllItems();
            return allItems.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            LoadAllItems();
            allItems.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get 
            {
                LoadAllItems();
                return allItems.Count;
            }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(T item)
        {
            IsChanged = true;

            Removed.Add(item);

            if (IsLoaded)
            {
                allItems.Remove(item);
            }

            return true;
        }

        #endregion



        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            LoadAllItems();
            return allItems.Cast<T>().GetEnumerator();
        }

        #endregion



        #region IEnumerable Members

        public List<T> GetAll()
        {
            LoadAllItems();
            return allItems.Cast<T>().ToList();
        }

        #endregion



        #region IList<T> Members

        public int IndexOf(T item)
        {
            LoadAllItems();
            return allItems.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            LoadAllItems();
            allItems.Insert(index, item);

            Added.Add(item);

            IsChanged = true;
        }

        public void RemoveAt(int index)
        {
            LoadAllItems();

            Removed.Add(allItems[index]);
            allItems.RemoveAt(index);

            IsChanged = true;
        }

        public T this[int index]
        {
            get
            {
                LoadAllItems();
                return allItems[index] as T;
            }
            set
            {
                LoadAllItems();
                allItems[index] = value;
            }
        }

        #endregion
    }
}
